import os
import json
import random
import time
import logging
from filelock import FileLock
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import subprocess


class BaseBot:
    def get_chrome_version(self):
        """自动获取 Chrome 的版本号"""
        try:
            # 获取当前 Chrome 版本
            version_output = subprocess.check_output(
                [
                    "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
                    "--version",
                ]
            )
            version = version_output.decode("utf-8").strip()
            # 例如：Google Chrome 139.0.7258.157 -> 139.0.7258.157
            return version.split(" ")[2]
        except Exception as e:
            print(f"无法获取 Chrome 版本号: {e}")
            return None

    def save_comment_count(self):
        lock_path = self.comment_count_path + ".lock"
        with FileLock(lock_path):
            # 读取现有文件数据
            if os.path.exists(self.comment_count_path):
                try:
                    with open(self.comment_count_path, "r", encoding="utf-8") as f:
                        existing_data = json.load(f)
                except Exception:
                    existing_data = {}
            else:
                existing_data = {}

            # 确保结构完整
            if self.class_name not in existing_data:
                existing_data[self.class_name] = {}
            existing_data[self.class_name][self.today] = self.comment_count

            with open(self.comment_count_path, "w", encoding="utf-8") as f:
                json.dump(existing_data, f, ensure_ascii=False, indent=2)

    def __init__(self, log_dir, comment_path, home_url):
        self.home_url = home_url
        self.class_name = self.__class__.__name__
        self.today = time.strftime("%Y-%m-%d")

        # 日志设置
        log_path = os.path.join(log_dir, "rainbot.log")
        self._setup_logger(log_path)

        # 评论计数文件
        self.comment_count_path = os.path.join(log_dir, "comment_count_daily.json")
        if os.path.exists(self.comment_count_path):
            with open(self.comment_count_path, "r", encoding="utf-8") as f:
                all_data = json.load(f)
        else:
            all_data = {}
        self.comment_count = all_data.get(self.class_name, {}).get(self.today, 0)
        self.comment_count_data = all_data

        # 加载评论语料
        try:
            with open(comment_path, "r", encoding="utf-8") as f:
                self.comments = json.load(f)
        except Exception as e:
            self.logger.warning(f"[{self.class_name}] 加载评论语料失败：{e}")
            self.comments = ["有趣", "赞", "支持一下", "感谢分享"]

    def get_random_comment(self):
        return random.choice(self.comments)

    def update_comment_count(self):
        if self.class_name not in self.comment_count_data:
            self.comment_count_data[self.class_name] = {}
        self.comment_count_data[self.class_name][self.today] = self.comment_count
        with open(self.comment_count_path, "w", encoding="utf-8") as f:
            json.dump(self.comment_count_data, f, ensure_ascii=False, indent=2)

    def load_cookies(self, driver, cookie_path):
        """
        从文件加载并注入 cookies。
        返回 True 表示成功注入至少 1 条；否则返回 False。
        """
        if os.path.exists(cookie_path):
            try:
                with open(cookie_path, "r", encoding="utf-8") as f:
                    cookies = json.load(f)
                    if not cookies:
                        self.logger.warning(f"[{self.class_name}] Cookie 文件为空")
                        return False
                    injected = 0
                    for cookie in cookies:
                        try:
                            driver.add_cookie(cookie)
                            injected += 1
                        except Exception as e:
                            self.logger.warning(
                                f"[{self.class_name}] 注入单条 cookie 失败：{e}"
                            )
                    if injected > 0:
                        self.logger.info(
                            f"[{self.class_name}] 成功注入 {injected} 条 cookies"
                        )
                        return True
                    else:
                        self.logger.warning(
                            f"[{self.class_name}] 未成功注入任何 cookie"
                        )
                        return False
            except Exception as e:
                self.logger.warning(f"[{self.class_name}] 加载 cookies 失败：{e}")
                return False
        else:
            self.logger.info(f"[{self.class_name}] 未找到 cookie 文件，跳过加载")
            return False

    def save_cookies(self, driver, cookie_path):
        try:
            cookies = driver.get_cookies()
            with open(cookie_path, "w", encoding="utf-8") as f:
                json.dump(cookies, f, ensure_ascii=False, indent=2)
            self.logger.info(
                f"[{self.class_name}] 已保存全量 Cookie，共 {len(cookies)} 条，登录流程完成"
            )
        except Exception as e:
            self.logger.warning(f"[{self.class_name}] 保存 cookies 失败：{e}")

    def load_cache(self, cache_path):
        if os.path.exists(cache_path):
            try:
                with open(cache_path, "r", encoding="utf-8") as f:
                    return json.load(f)
            except Exception as e:
                self.logger.warning(f"[{self.class_name}] 加载缓存失败：{e}")
        return {}

    def save_cache(self, cache_path, cache_data):
        try:
            with open(cache_path, "w", encoding="utf-8") as f:
                json.dump(cache_data, f, ensure_ascii=False, indent=2)
            self.logger.info(
                f"[{self.class_name}] 已保存缓存，共 {len(cache_data)} 条链接"
            )
        except Exception as e:
            self.logger.warning(f"[{self.class_name}] 保存缓存失败：{e}")

    def try_with_retry(self, func, retries=3, delay=1.5):
        for i in range(retries):
            try:
                return func()
            except Exception as e:
                self.logger.warning(f"[{self.class_name}] 第 {i+1} 次尝试失败：{e}")
                time.sleep(delay)
        self.logger.error(f"[{self.class_name}] 所有重试失败")
        return None

    def wait_for_element(self, driver, by, value, timeout=15):
        try:
            return WebDriverWait(driver, timeout).until(
                EC.presence_of_element_located((by, value))
            )
        except TimeoutException:
            self.logger.warning(f"[{self.class_name}] 等待元素超时：{value}")
            return None

    def sleep_random(self, base=1.0, jitter=0.8):
        t = base + random.uniform(0, jitter)
        self.logger.debug(f"[{self.class_name}] 随机休眠 {t:.2f} 秒")
        time.sleep(t)

    def is_logged_in(self, driver, check_func):
        try:
            return check_func(driver)
        except Exception as e:
            self.logger.warning(f"[{self.class_name}] 登录状态检查出错：{e}")
            return False

    def remove_cache(self, url):
        try:
            if os.path.exists(self.cache_path):
                with open(self.cache_path, "r", encoding="utf-8") as f:
                    cached = json.load(f)
                if url in cached:
                    del cached[url]
                    with open(self.cache_path, "w", encoding="utf-8") as f:
                        json.dump(cached, f, ensure_ascii=False, indent=2)
                    self.logger.info(
                        f"[{self.class_name}] 已从缓存中移除已评论链接：{url}"
                    )
        except Exception as e:
            self.logger.warning(f"[{self.class_name}] 移除链接缓存失败: {e}")

    def exit(self, num=0):
        if self.driver:
            self.driver.quit()
        exit(num)

    def ensure_login(self, driver, cookie_path, check_func):
        driver.get(self.home_url)  # 加载平台首页，确保 driver 初始化
        loaded = self.load_cookies(driver, cookie_path)
        if not loaded:
            self.logger.info(f"[{self.class_name}] Cookie 加载失败或无效，进入登录流程")
            input(f"[{self.class_name}] 请在当前页面完成登录后按回车继续...")
            self.save_cookies(driver, cookie_path)
            return

        driver.refresh()
        self.sleep_random()
        if self.is_logged_in(driver, check_func):
            self.logger.info(f"[{self.class_name}] 已登录")
        else:
            self.logger.info(f"[{self.class_name}] Cookie 登录未生效，请手动操作登录")
            input(f"[{self.class_name}] 完成登录后按回车继续...")
            self.save_cookies(driver, cookie_path)

    def _setup_logger(self, log_path):
        logger = logging.getLogger(self.class_name)
        logger.setLevel(logging.DEBUG)
        formatter = logging.Formatter(
            "%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
        )

        if not logger.handlers:
            # File handler: INFO 及以上写入文件
            fh = logging.FileHandler(log_path, encoding="utf-8")
            fh.setLevel(logging.INFO)
            fh.setFormatter(formatter)
            logger.addHandler(fh)

            # Stream handler: ERROR 及以上输出到终端
            sh = logging.StreamHandler()
            sh.setLevel(logging.ERROR)
            sh.setFormatter(formatter)
            logger.addHandler(sh)

        self.logger = logger

    # BaseBot 中添加
    def login(self):
        raise NotImplementedError("请在子类中实现 login() 方法")
